package com.glsc.ngateway.platform.service.cmdb;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import com.alibaba.fastjson.JSON;
import com.glsc.ngateway.common.api.common.enums.DictConstant;
import com.glsc.ngateway.common.api.platform.dto.user.PlatformUserDto;
import com.glsc.ngateway.common.base.domain.mysql.cmdb.*;
import com.glsc.ngateway.common.base.domainmapper.CmdbCpuStandardMapper;
import com.glsc.ngateway.common.base.domainmapper.CmdbDiskStandardMapper;
import com.glsc.ngateway.common.base.domainmapper.CmdbMemoryStandardMapper;
import com.glsc.ngateway.common.base.domainmapper.CmdbPartStandardMapper;
import com.glsc.ngateway.common.base.dto.cmdb.*;
import com.glsc.ngateway.common.base.repo.mysql.cmdb.*;
import com.glsc.ngateway.platform.utils.AssertOverride;
import com.glsc.ngateway.platform.utils.report.ReportFileConfigEnum;
import com.glsc.ngateway.platform.utils.report.ReportFileUtil;
import com.glsc.ngateway.platform.utils.report.ReportUploadContext;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.glsc.ngateway.common.api.common.enums.DictConstant.CMDB_CPU_STANDARD;
import static com.glsc.ngateway.common.api.common.enums.DictConstant.CMDB_MEMORY_STANDARD;

@Service
@Slf4j
public class CmdbExcelImportExportService {

    private static String DATE_FORMAT = "yyyy-MM-dd";

    @Autowired
    private CmdbCpuStandardRepository cmdbCpuStandardRepository;

    @Autowired
    private SequenceProductService sequenceProductService;

    @Autowired
    private CmdbMemoryStandardRepository cmdbMemoryStandardRepository;

    @Autowired
    private CmdbDiskStandardRepository cmdbDiskStandardRepository;

    @Autowired
    private CmdbPartStandardRepository cmdbPartStandardRepository;

    @Autowired
    private CmdbCpuDetailRepository cmdbCpuDetailRepository;

    @Autowired
    private CmdbMemoryDetailRepository cmdbMemoryDetailRepository;

    @Autowired
    private CmdbDiskDetailRepository cmdbDiskDetailRepository;

    @Autowired
    private CmdbPartDetailRepository cmdbPartDetailRepository;

    @Autowired
    private CmdbDeviceStandardRepository cmdbDeviceStandardRepository;

    @Autowired
    private CmdbCpuStandardDeviceStandardRelateRepository cmdbCpuStandardDeviceStandardRelateRepository;

    @Autowired
    private CmdbMemoryStandardDeviceStandardRelateRepository cmdbMemoryStandardDeviceStandardRelateRepository;

    @Autowired
    private CmdbDiskStandardDeviceStandardRelateRepository cmdbDiskStandardDeviceStandardRelateRepository;

    @Autowired
    private CmdbPartStandardDeviceStandardRelateRepository cmdbPartStandardDeviceStandardRelateRepository;

    @Autowired
    private CmdbDeviceDetailRepository cmdbDeviceDetailRepository;

    @Autowired
    private CmdbDeviceTypeRepository cmdbDeviceTypeRepository;

    @Autowired
    private CmdbMachineRoomRepository cmdbMachineRoomRepository;

    @Autowired
    private CmdbPartTypeService cmdbPartTypeService;

    @Autowired
    private CmdbChangeLogRepository cmdbChangeLogRepository;

    /**
     * cmbc cpu规格 上传文件导入
     */
    @Transactional
    public ReportUploadContext<CmdbCpuStandardImportDto, String> cpuStandardUpload(MultipartFile file, Integer type, PlatformUserDto user) throws IOException {
        ReportUploadContext<CmdbCpuStandardImportDto, String> context = ReportUploadContext.<CmdbCpuStandardImportDto, String>builder()
                .config(ReportFileConfigEnum.CMDB_CPU_STANDARD)
                .file(file)
                .check(this::checkCpuStandard)
                .build();
        ReportFileUtil.readSingleExcel(context);
        AssertOverride.notEmpty(context.getReadRes(), "上传内容不可为空");

        List<CmdbCpuStandard> cmdbCpuStandardList = CmdbCpuStandardMapper.INSTANCE.dto2domain(context.getReadRes());
        cmdbCpuStandardList.forEach(e -> {
            e.setCpuStandardCode(sequenceProductService.buildSequence(DictConstant.CMDB_CPU_STANDARD));
            e.setIsDelete(0);
        });
        //全部检查通过后添加或修改数据
        if (Objects.nonNull(type) && type == 1 && context.isCheckPass()) {
            cmdbCpuStandardRepository.saveAll(cmdbCpuStandardList);
            cmdbChangeLog(JSON.toJSONString(cmdbCpuStandardList), DictConstant.ADD_STRING, CMDB_CPU_STANDARD, user.getUsername());
        }
        return context;
    }

    /**
     * 校验cpu规格上传数据
     *
     * @param dto
     */
    public void checkCpuStandard(CmdbCpuStandardImportDto dto) {
        AssertOverride.isTrue(StringUtils.isNotEmpty(dto.getPartNum()), dto.getName() + "的[配件号]不可为空");
        CmdbCpuStandard standard = this.findCpuStandardByPartNum(dto.getPartNum());
        AssertOverride.isNull(standard, dto.getPartNum() + "规格已存在");
    }

    /**
     * cmbc 内存规格 上传文件导入
     */
    @Transactional
    public ReportUploadContext<CmdbMemoryStandardImportDto, String> memoryStandardUpload(MultipartFile file, Integer type, PlatformUserDto user) throws IOException {
        ReportUploadContext<CmdbMemoryStandardImportDto, String> context = ReportUploadContext.<CmdbMemoryStandardImportDto, String>builder()
                .config(ReportFileConfigEnum.CMDB_MEMORY_STANDARD)
                .file(file)
                .check(this::checkMemoryStandard)
                .build();
        ReportFileUtil.readSingleExcel(context);
        AssertOverride.notEmpty(context.getReadRes(), "上传内容不可为空");

        List<CmdbMemoryStandard> cmdbMemoryStandardList = CmdbMemoryStandardMapper.INSTANCE.dto2domain(context.getReadRes());
        cmdbMemoryStandardList.forEach(e -> {
            e.setMemoryStandardCode(sequenceProductService.buildSequence(CMDB_MEMORY_STANDARD));
            e.setIsDelete(0);
        });
        //全部检查通过后添加或修改数据
        if (Objects.nonNull(type) && type == 1 && context.isCheckPass()) {
            cmdbMemoryStandardRepository.saveAll(cmdbMemoryStandardList);
            cmdbChangeLog(JSON.toJSONString(cmdbMemoryStandardList), DictConstant.ADD_STRING, CMDB_MEMORY_STANDARD, user.getUsername());
        }
        return context;
    }

    /**
     * 校验内存规格上传数据
     *
     * @param dto
     */
    public void checkMemoryStandard(CmdbMemoryStandardImportDto dto) {
        AssertOverride.isTrue(StringUtils.isNotEmpty(dto.getPartNum()), dto.getName() + "的[配件号]不可为空");
        CmdbMemoryStandard standard = this.findMemoryStandardByPartNum(dto.getPartNum());
        AssertOverride.isNull(standard, dto.getPartNum() + "规格已存在");
    }

    /**
     * cmbc 硬盘规格 上传文件导入
     */
    @Transactional
    public ReportUploadContext<CmdbDiskStandardImportDto, String> diskStandardUpload(MultipartFile file, Integer type, PlatformUserDto user) throws IOException {
        ReportUploadContext<CmdbDiskStandardImportDto, String> context = ReportUploadContext.<CmdbDiskStandardImportDto, String>builder()
                .config(ReportFileConfigEnum.CMDB_DISK_STANDARD)
                .file(file)
                .check(this::checkDiskStandard)
                .build();
        ReportFileUtil.readSingleExcel(context);
        AssertOverride.notEmpty(context.getReadRes(), "上传内容不可为空");

        List<CmdbDiskStandard> cmdbDiskStandardList = CmdbDiskStandardMapper.INSTANCE.dto2domain(context.getReadRes());
        cmdbDiskStandardList.forEach(e -> {
            e.setDiskStandardCode(sequenceProductService.buildSequence(DictConstant.CMDB_DISK_STANDARD));
            e.setIsDelete(0);
        });
        //全部检查通过后添加或修改数据
        if (Objects.nonNull(type) && type == 1 && context.isCheckPass()) {
            cmdbDiskStandardRepository.saveAll(cmdbDiskStandardList);
            cmdbChangeLog(JSON.toJSONString(cmdbDiskStandardList), DictConstant.ADD_STRING, DictConstant.CMDB_DISK_STANDARD, user.getUsername());
        }
        return context;
    }

    /**
     * 校验内存规格上传数据
     *
     * @param dto
     */
    public void checkDiskStandard(CmdbDiskStandardImportDto dto) {
        AssertOverride.isTrue(StringUtils.isNotEmpty(dto.getPartNum()), dto.getName() + "的[配件号]不可为空");
        CmdbDiskStandard standard = this.findDiskStandardByPartNum(dto.getPartNum());
        AssertOverride.isNull(standard, dto.getPartNum() + "规格已存在");
    }

    /**
     * cmbc 配件规格 上传文件导入
     */
    @Transactional
    public ReportUploadContext<CmdbPartStandardImportDto, String> partStandardUpload(MultipartFile file, Integer type, PlatformUserDto user) throws IOException {
        ReportUploadContext<CmdbPartStandardImportDto, String> context = ReportUploadContext.<CmdbPartStandardImportDto, String>builder()
                .config(ReportFileConfigEnum.CMDB_PART_STANDARD)
                .file(file)
                .check(this::checkPartStandard)
                .build();
        ReportFileUtil.readSingleExcel(context);
        AssertOverride.notEmpty(context.getReadRes(), "上传内容不可为空");

        List<CmdbPartType> typeList = cmdbPartTypeService.findAll();
        AssertOverride.notEmpty(typeList, "未查询到配件类型信息");

        Map<String, Integer> typeIdMap = typeList.stream().collect(Collectors.toMap(CmdbPartType::getPartTypeName, CmdbPartType::getPartTypeId, (i, j) -> j));
        Map<String, CmdbPartStandardImportDto> dtoMap = context.getReadRes().stream().collect(Collectors.toMap(CmdbPartStandardImportDto::getStandardNum, e -> e, (i, j) -> j));
        //全部检查通过后添加或修改数据
        List<CmdbPartStandard> cmdbPartStandardList = CmdbPartStandardMapper.INSTANCE.dto2domain(context.getReadRes());
        cmdbPartStandardList.forEach(e -> {
            e.setPartStandardCode(sequenceProductService.buildSequence(DictConstant.CMDB_PART_STANDARD));
            e.setIsDelete(0);
            e.setPartTypeId(typeIdMap.get(dtoMap.get(e.getStandardNum()).getPartType()));
        });
        //全部检查通过后添加或修改数据
        if (Objects.nonNull(type) && type == 1 && context.isCheckPass()) {
            cmdbPartStandardRepository.saveAll(cmdbPartStandardList);
            cmdbChangeLog(JSON.toJSONString(cmdbPartStandardList), DictConstant.ADD_STRING, DictConstant.CMDB_PART_STANDARD, user.getUsername());
        }
        context.getReadRes().forEach(e -> {
            e.setPartTypeId(typeIdMap.get(e.getPartType()));
        });
        return context;
    }

    /**
     * 校验内存规格上传数据
     *
     * @param dto
     */
    public void checkPartStandard(CmdbPartStandardImportDto dto) {
        AssertOverride.isTrue(StringUtils.isNotEmpty(dto.getPartNum()), dto.getName() + "的[配件号]不可为空");
        CmdbPartStandard standard = this.findPartStandardByPartNum(dto.getPartNum());
        AssertOverride.isNull(standard, dto.getPartNum() + "规格已存在");
    }

    /**
     * 服务器规格导入
     *
     * @param file
     * @param type
     * @return
     * @throws IOException
     */
    @Transactional
    public List<CmdbDeviceStandard> deviceStandardUpload(MultipartFile file, Integer type, PlatformUserDto user) throws IOException {
        ReportUploadContext<CmdbDeviceStandardImportDto, String> context = ReportUploadContext.<CmdbDeviceStandardImportDto, String>builder()
                .config(ReportFileConfigEnum.CMDB_DEVICE_STANDARD)
                .file(file)
                .check(this::checkDeviceStandard)
                .build();
        ReportFileUtil.readSingleExcel(context);
        AssertOverride.notEmpty(context.getReadRes(), "上传内容不可为空");

        List<CmdbDeviceStandard> deviceStandardsList = Lists.newArrayList();
        List<CmdbCpuStandardDeviceStandardRelate> cpuRelateList = Lists.newArrayList();
        List<CmdbMemoryStandardDeviceStandardRelate> memoryRelateList = Lists.newArrayList();
        List<CmdbDiskStandardDeviceStandardRelate> diskRelateList = Lists.newArrayList();
        List<CmdbPartStandardDeviceStandardRelate> partRelateList = Lists.newArrayList();

        context.getReadRes().forEach(e -> {

            List<CmdbDeviceStandard> savedDeviceList = cmdbDeviceStandardRepository.findAll(Example.of(CmdbDeviceStandard.builder().standardName(e.getStandardName()).build()));
            AssertOverride.isTrue(!CollectionUtil.isNotEmpty(savedDeviceList), e.getStandardName() + "设备规格名称已存在,请修改!");
            String deviceStandardCode = sequenceProductService.buildSequence(DictConstant.CMDB_DEVICE_STANDARD);
            if (type == 0) {
                deviceStandardCode = null;
            }
            CmdbDeviceStandard cmdbDeviceStandard = CmdbDeviceStandard.builder().deviceStandardCode(deviceStandardCode).brand(e.getBrand()).model(e.getModel())
                    .uAmount(e.getUAmount()).cpuPortAmount(e.getCpuPortAmount()).memoryPortAmount(e.getMemoryPortAmount()).diskPortAmount(e.getDiskPortAmount()).pciPortAmount(e.getPciPortAmount())
                    .elePortAmount(e.getElcPortAmount()).standardName(e.getStandardName()).cpuAmount(e.getCpuAmount()).memoryAmount(e.getMemoryAmount()).isDelete(0).build();
            deviceStandardsList.add(cmdbDeviceStandard);

            if (StringUtils.isNotBlank(e.getCpuPartNum())) {
                CmdbCpuStandard cpuStandard = findCpuStandardByPartNum(e.getCpuPartNum());
                AssertOverride.notNull(cpuStandard, "未查询到规格配件号[" + e.getCpuPartNum() + "]的规格信息");
                CmdbCpuStandardDeviceStandardRelate cmdbCpuStandardDeviceStandardRelate = CmdbCpuStandardDeviceStandardRelate.builder().amount(e.getCpuAmount()).cpuStandardCode(cpuStandard.getCpuStandardCode())
                        .deviceStandardCode(deviceStandardCode).isDelete(0).build();
                cpuRelateList.add(cmdbCpuStandardDeviceStandardRelate);
                cmdbDeviceStandard.setCpuStandard(cpuStandard);
                cmdbDeviceStandard.setCpuStandardCode(cpuStandard.getCpuStandardCode());
                cmdbDeviceStandard.setCpuStandardName(cpuStandard.getName());
            }

            if (StringUtils.isNotBlank(e.getMemoryPartNum())) {
                CmdbMemoryStandard memoryStandard = findMemoryStandardByPartNum(e.getMemoryPartNum());
                AssertOverride.notNull(memoryStandard, "未查询到规格配件号[" + e.getMemoryPartNum() + "]的规格信息");
                CmdbMemoryStandardDeviceStandardRelate cmdbMemoryStandardDeviceStandardRelate = CmdbMemoryStandardDeviceStandardRelate.builder().amount(e.getMemoryAmount()).deviceStandardCode(deviceStandardCode)
                        .isDelete(0).memoryStandardCode(memoryStandard.getMemoryStandardCode()).build();
                memoryRelateList.add(cmdbMemoryStandardDeviceStandardRelate);
                cmdbDeviceStandard.setMemoryStandard(memoryStandard);
                cmdbDeviceStandard.setMemoryStandard(memoryStandard);
                cmdbDeviceStandard.setMemoryStandardCode(memoryStandard.getMemoryStandardCode());
                cmdbDeviceStandard.setMemoryStandardName(memoryStandard.getName());
            }

            List<CmdbDiskStandardDeviceStandardRelate> diskTempList = Lists.newArrayList();
            cmdbDeviceStandard.setDiskStandardRelate(diskTempList);
            if (StringUtils.isNotBlank(e.getDiskPartNum1())) {
                CmdbDiskStandard diskStandard1 = findDiskStandardByPartNum(e.getDiskPartNum1());
                AssertOverride.notNull(diskStandard1, "未查询到规格配件号[" + e.getDiskPartNum1() + "]的规格信息");
                CmdbDiskStandardDeviceStandardRelate cmdbDiskStandardDeviceStandardRelate = CmdbDiskStandardDeviceStandardRelate.builder().amount(e.getDiskAmount1()).deviceStandardCode(deviceStandardCode)
                        .diskStandardName(diskStandard1.getName()).diskStandardCode(diskStandard1.getDiskStandardCode()).isDelete(0).build();
                diskRelateList.add(cmdbDiskStandardDeviceStandardRelate);
                diskTempList.add(cmdbDiskStandardDeviceStandardRelate);
            }

            if (StringUtils.isNotBlank(e.getDiskPartNum2())) {
                CmdbDiskStandard diskStandard2 = findDiskStandardByPartNum(e.getDiskPartNum2());
                AssertOverride.notNull(diskStandard2, "未查询到规格配件号[" + e.getDiskPartNum2() + "]的规格信息");
                CmdbDiskStandardDeviceStandardRelate cmdbDiskStandardDeviceStandardRelate = CmdbDiskStandardDeviceStandardRelate.builder().amount(e.getDiskAmount2()).deviceStandardCode(deviceStandardCode)
                        .diskStandardName(diskStandard2.getName()).diskStandardCode(diskStandard2.getDiskStandardCode()).isDelete(0).build();
                diskRelateList.add(cmdbDiskStandardDeviceStandardRelate);
                diskTempList.add(cmdbDiskStandardDeviceStandardRelate);
            }
            List<CmdbPartStandardDeviceStandardRelate> partTempList = Lists.newArrayList();
            cmdbDeviceStandard.setPartStandardRelate(partTempList);
            //硬盘笼子规格
            if (StringUtils.isNotBlank(e.getDiskCagePartNum())) {
                CmdbPartStandardDeviceStandardRelate cmdbPartStandardDeviceStandardRelate = convertCmdbPartStandardDeviceStandardRelate(e.getDiskCagePartNum(), "硬盘笼子", e,
                        deviceStandardCode, e.getDiskCageAmount());
                partRelateList.add(cmdbPartStandardDeviceStandardRelate);
                partTempList.add(cmdbPartStandardDeviceStandardRelate);
            }

            //Pci笼子规格
            if (StringUtils.isNotBlank(e.getPciCagePartNum())) {
                CmdbPartStandardDeviceStandardRelate cmdbPartStandardDeviceStandardRelate = convertCmdbPartStandardDeviceStandardRelate(e.getPciCagePartNum(), "Pci笼子", e, deviceStandardCode,
                        e.getPciCageAmount());
                partRelateList.add(cmdbPartStandardDeviceStandardRelate);
                partTempList.add(cmdbPartStandardDeviceStandardRelate);
            }

            //阵列卡规格
            if (StringUtils.isNotBlank(e.getArrayCardPartNum())) {
                CmdbPartStandardDeviceStandardRelate cmdbPartStandardDeviceStandardRelate = convertCmdbPartStandardDeviceStandardRelate(e.getArrayCardPartNum(), "阵列卡", e, deviceStandardCode,
                        e.getArrayCardAmount());
                partRelateList.add(cmdbPartStandardDeviceStandardRelate);
                partTempList.add(cmdbPartStandardDeviceStandardRelate);
            }

            //阵列卡电池
            if (StringUtils.isNotBlank(e.getArrayCardCellPartNum())) {
                CmdbPartStandardDeviceStandardRelate cmdbPartStandardDeviceStandardRelate = convertCmdbPartStandardDeviceStandardRelate(e.getArrayCardCellPartNum(), "阵列卡电池", e, deviceStandardCode,
                        e.getArrayCardCellAmount());
                partRelateList.add(cmdbPartStandardDeviceStandardRelate);
                partTempList.add(cmdbPartStandardDeviceStandardRelate);
            }

            //网卡规格1
            if (StringUtils.isNotBlank(e.getNetworkCardPartNum1())) {
                CmdbPartStandardDeviceStandardRelate cmdbPartStandardDeviceStandardRelate = convertCmdbPartStandardDeviceStandardRelate(e.getNetworkCardPartNum1(), "网卡1", e, deviceStandardCode,
                        e.getNetworkCardAmount1());
                partRelateList.add(cmdbPartStandardDeviceStandardRelate);
                partTempList.add(cmdbPartStandardDeviceStandardRelate);
            }

            //网卡规格2
            if (StringUtils.isNotBlank(e.getNetworkCardPartNum2())) {
                CmdbPartStandardDeviceStandardRelate cmdbPartStandardDeviceStandardRelate = convertCmdbPartStandardDeviceStandardRelate(e.getNetworkCardPartNum2(), "网卡2", e, deviceStandardCode,
                        e.getNetworkCardAmount2());
                partRelateList.add(cmdbPartStandardDeviceStandardRelate);
                partTempList.add(cmdbPartStandardDeviceStandardRelate);
            }

            //HBA卡规格
            if (StringUtils.isNotBlank(e.getHbaCardPartNum())) {
                CmdbPartStandardDeviceStandardRelate cmdbPartStandardDeviceStandardRelate = convertCmdbPartStandardDeviceStandardRelate(e.getHbaCardPartNum(), "HBA卡", e, deviceStandardCode,
                        e.getHbaCardAmount());
                partRelateList.add(cmdbPartStandardDeviceStandardRelate);
                partTempList.add(cmdbPartStandardDeviceStandardRelate);
            }

            //电源规格
            if (StringUtils.isNotBlank(e.getPowerSourcePartNum())) {
                CmdbPartStandardDeviceStandardRelate cmdbPartStandardDeviceStandardRelate = convertCmdbPartStandardDeviceStandardRelate(e.getPowerSourcePartNum(), "电源", e, deviceStandardCode,
                        e.getPowerSourceAmount());
                partRelateList.add(cmdbPartStandardDeviceStandardRelate);
                partTempList.add(cmdbPartStandardDeviceStandardRelate);
            }

            //gpu规格
            if (StringUtils.isNotBlank(e.getGpuPartNum())) {
                CmdbPartStandardDeviceStandardRelate cmdbPartStandardDeviceStandardRelate = convertCmdbPartStandardDeviceStandardRelate(e.getGpuPartNum(), "gpu", e, deviceStandardCode,
                        e.getGpuAmount());
                partRelateList.add(cmdbPartStandardDeviceStandardRelate);
                partTempList.add(cmdbPartStandardDeviceStandardRelate);
            }

            //gpu笼子规格
            if (StringUtils.isNotBlank(e.getGpuCagePartNum())) {
                CmdbPartStandardDeviceStandardRelate cmdbPartStandardDeviceStandardRelate = convertCmdbPartStandardDeviceStandardRelate(e.getGpuCagePartNum(), "gpu笼子", e, deviceStandardCode,
                        e.getGpuCageAmount());
                partRelateList.add(cmdbPartStandardDeviceStandardRelate);
                partTempList.add(cmdbPartStandardDeviceStandardRelate);
            }

        });

        //全部检查通过后添加或修改数据
        if (Objects.nonNull(type) && type == 1 && context.isCheckPass()) {
            if (CollectionUtil.isNotEmpty(deviceStandardsList)) {
                cmdbDeviceStandardRepository.saveAll(deviceStandardsList);
                cmdbChangeLog(JSON.toJSONString(deviceStandardsList), DictConstant.ADD_STRING, DictConstant.CMDB_DEVICE_STANDARD, user.getUsername());
            }
            if (CollectionUtil.isNotEmpty(cpuRelateList)) {
                cmdbCpuStandardDeviceStandardRelateRepository.saveAll(cpuRelateList);
                cmdbChangeLog(JSON.toJSONString(cpuRelateList), DictConstant.ADD_STRING, DictConstant.CMDB_CPU_STANDARD_DEVICE_STANDARD_RELATE, user.getUsername());
            }
            if (CollectionUtil.isNotEmpty(memoryRelateList)) {
                cmdbMemoryStandardDeviceStandardRelateRepository.saveAll(memoryRelateList);
                cmdbChangeLog(JSON.toJSONString(memoryRelateList), DictConstant.ADD_STRING, DictConstant.CMDB_MEMORY_STANDARD_DEVICE_STANDARD_RELATE, user.getUsername());
            }
            if (CollectionUtil.isNotEmpty(diskRelateList)) {
                cmdbDiskStandardDeviceStandardRelateRepository.saveAll(diskRelateList);
                cmdbChangeLog(JSON.toJSONString(diskRelateList), DictConstant.ADD_STRING, DictConstant.CMDB_DISK_STANDARD_DEVICE_STANDARD_RELATE, user.getUsername());
            }
            if (CollectionUtil.isNotEmpty(partRelateList)) {
                cmdbPartStandardDeviceStandardRelateRepository.saveAll(partRelateList);
                cmdbChangeLog(JSON.toJSONString(partRelateList), DictConstant.ADD_STRING, DictConstant.CMDB_PART_STANDARD_DEVICE_STANDARD_RELATE, user.getUsername());
            }

        }
        return deviceStandardsList;
    }


    /**
     * 校验服务器规格上传数据
     *
     * @param dto
     */
    public void checkDeviceStandard(CmdbDeviceStandardImportDto dto) {
        AssertOverride.isTrue(StringUtils.isNotEmpty(dto.getStandardName()), "[名称]不可为空");
    }

    /**
     * cmbc cpu详情 上传文件导入
     */
    @Transactional
    public ReportUploadContext<CmdbCommonDetailImportDto, String> cpuDetailUpload(MultipartFile file, Integer type, PlatformUserDto user) throws IOException {
        ReportUploadContext<CmdbCommonDetailImportDto, String> context = ReportUploadContext.<CmdbCommonDetailImportDto, String>builder()
                .config(ReportFileConfigEnum.CMDB_CPU_DETAIL)
                .file(file)
                .check(this::checkDetailImport)
                .build();
        ReportFileUtil.readSingleExcel(context);
        AssertOverride.notEmpty(context.getReadRes(), "上传内容不可为空");

        List<CmdbCommonDetailImportDto> readResList = context.getReadRes();
        List<CmdbCpuDetail> insertDetailList = Lists.newArrayList();
        Map<String, CmdbMachineRoom> roomMap = new HashMap<>();
        for (CmdbCommonDetailImportDto dto : readResList) {
            CmdbCpuStandard cpuStandard = findCpuStandardByPartNum(dto.getPartNum());
            AssertOverride.notNull(cpuStandard, "未查询到规格配件号[" + dto.getPartNum() + "]的规格信息");
            //dto.setPartNum(cpuStandard.getPartNum());
            if (StringUtils.isNotBlank(dto.getMachineRoom())) {
                if (roomMap.containsKey(dto.getMachineRoom())) {
                    dto.setLocationId(roomMap.get(dto.getMachineRoom()).getId());
                } else {
                    CmdbMachineRoom room = findCmdbMachineRoomByName(dto.getMachineRoom());
                    AssertOverride.notNull(room, "未查询到名称为:" + dto.getMachineRoom() + "的机房信息");
                    dto.setLocationId(room.getId());
                    roomMap.put(dto.getMachineRoom(), room);
                }
            }
            CmdbCpuDetail cmdbCpuDetail = CmdbCpuDetail.builder().cpuDetailCode(sequenceProductService.buildSequence(DictConstant.CMDB_CPU_DETAIL)).cpuStandardCode(cpuStandard.getCpuStandardCode()).isDelete(0).locationId(dto.getLocationId())
                    .lifeStatus(0).sn(dto.getSn()).build();
            insertDetailList.add(cmdbCpuDetail);
            cmdbCpuDetail.setExpirateDate(convertDate(dto.getExpirateDate()));
            cmdbCpuDetail.setManufactDate(convertDate(dto.getManufactDate()));
        }
        //全部检查通过后添加或修改数据
        if (Objects.nonNull(type) && type == 1 && context.isCheckPass() && CollectionUtil.isNotEmpty(insertDetailList)) {
            cmdbCpuDetailRepository.saveAll(insertDetailList);
            cmdbChangeLog(JSON.toJSONString(insertDetailList), DictConstant.ADD_STRING, DictConstant.CMDB_CPU_DETAIL, user.getUsername());
        }
        return context;
    }

    private LocalDate convertDate(String date) {
        if (StringUtils.isNotBlank(date)) {
            return LocalDateTimeUtil.parseDate(date, DATE_FORMAT);
        }
        return null;
    }

    public void checkDetailImport(CmdbCommonDetailImportDto dto) {
        AssertOverride.isTrue(StringUtils.isNotBlank(dto.getPartNum()), "规格配件号不可为空");
    }

    /**
     * cmbc 内存详情 上传文件导入
     */
    @Transactional
    public ReportUploadContext<CmdbCommonDetailImportDto, String> memoryDetailUpload(MultipartFile file, Integer type, PlatformUserDto user) throws IOException {
        ReportUploadContext<CmdbCommonDetailImportDto, String> context = ReportUploadContext.<CmdbCommonDetailImportDto, String>builder()
                .config(ReportFileConfigEnum.CMDB_MEMORY_DETAIL)
                .file(file)
                .check(this::checkDetailImport)
                .build();
        ReportFileUtil.readSingleExcel(context);
        AssertOverride.notEmpty(context.getReadRes(), "上传内容不可为空");

        List<CmdbCommonDetailImportDto> readResList = context.getReadRes();
        List<CmdbMemoryDetail> insertDetailList = Lists.newArrayList();
        Map<String, CmdbMachineRoom> roomMap = new HashMap<>();

        for (CmdbCommonDetailImportDto dto : readResList) {
            CmdbMemoryStandard memoryStandard = findMemoryStandardByPartNum(dto.getPartNum());
            AssertOverride.notNull(memoryStandard, "未查询到规格配件号[" + dto.getPartNum() + "]的规格信息");
            //dto.setPartNum(memoryStandard.getPartNum());
            if (StringUtils.isNotBlank(dto.getMachineRoom())) {
                if (roomMap.containsKey(dto.getMachineRoom())) {
                    dto.setLocationId(roomMap.get(dto.getMachineRoom()).getId());
                } else {
                    CmdbMachineRoom room = findCmdbMachineRoomByName(dto.getMachineRoom());
                    AssertOverride.notNull(room, "未查询到名称为:" + dto.getMachineRoom() + "的机房信息");
                    dto.setLocationId(room.getId());
                    roomMap.put(dto.getMachineRoom(), room);
                }
            }

            CmdbMemoryDetail cmdbMemoryDetail = CmdbMemoryDetail.builder().memoryDetailCode(sequenceProductService.buildSequence(DictConstant.CMDB_MEMORY_DETAIL)).memoryStandardCode(memoryStandard.getMemoryStandardCode()).isDelete(0).locationId(dto.getLocationId())
                    .lifeStatus(0).sn(dto.getSn()).build();
            insertDetailList.add(cmdbMemoryDetail);
            cmdbMemoryDetail.setExpirateDate(convertDate(dto.getExpirateDate()));
            cmdbMemoryDetail.setManufactDate(convertDate(dto.getManufactDate()));
        }
        //全部检查通过后添加或修改数据
        if (Objects.nonNull(type) && type == 1 && context.isCheckPass() && CollectionUtil.isNotEmpty(insertDetailList)) {
            cmdbMemoryDetailRepository.saveAll(insertDetailList);
            cmdbChangeLog(JSON.toJSONString(insertDetailList), DictConstant.ADD_STRING, DictConstant.CMDB_MEMORY_DETAIL, user.getUsername());
        }
        return context;
    }


    /**
     * cmbc 硬盘详情 上传文件导入
     */
    @Transactional
    public ReportUploadContext<CmdbCommonDetailImportDto, String> diskDetailUpload(MultipartFile file, Integer type, PlatformUserDto user) throws IOException {
        ReportUploadContext<CmdbCommonDetailImportDto, String> context = ReportUploadContext.<CmdbCommonDetailImportDto, String>builder()
                .config(ReportFileConfigEnum.CMDB_DISK_DETAIL)
                .file(file)
                .check(this::checkDetailImport)
                .build();
        ReportFileUtil.readSingleExcel(context);
        AssertOverride.notEmpty(context.getReadRes(), "上传内容不可为空");

        List<CmdbCommonDetailImportDto> readResList = context.getReadRes();
        List<CmdbDiskDetail> insertDetailList = Lists.newArrayList();
        Map<String, CmdbMachineRoom> roomMap = new HashMap<>();

        for (CmdbCommonDetailImportDto dto : readResList) {
            CmdbDiskStandard diskStandard = findDiskStandardByPartNum(dto.getPartNum());
            AssertOverride.notNull(diskStandard, "未查询到规格配件号[" + dto.getPartNum() + "]的规格信息");
            //dto.setPartNum(diskStandard.getPartNum());
            if (StringUtils.isNotBlank(dto.getMachineRoom())) {
                if (roomMap.containsKey(dto.getMachineRoom())) {
                    dto.setLocationId(roomMap.get(dto.getMachineRoom()).getId());
                } else {
                    CmdbMachineRoom room = findCmdbMachineRoomByName(dto.getMachineRoom());
                    AssertOverride.notNull(room, "未查询到名称为:" + dto.getMachineRoom() + "的机房信息");
                    dto.setLocationId(room.getId());
                    roomMap.put(dto.getMachineRoom(), room);
                }
            }

            CmdbDiskDetail cmdbDiskDetail = CmdbDiskDetail.builder().diskDetailCode(sequenceProductService.buildSequence(DictConstant.CMDB_DISK_DETAIL)).diskStandardCode(diskStandard.getDiskStandardCode()).isDelete(0).locationId(dto.getLocationId())
                    .lifeStatus(0).sn(dto.getSn()).build();
            insertDetailList.add(cmdbDiskDetail);
            cmdbDiskDetail.setExpirateDate(convertDate(dto.getExpirateDate()));
            cmdbDiskDetail.setManufactDate(convertDate(dto.getManufactDate()));
        }
        //全部检查通过后添加或修改数据
        if (Objects.nonNull(type) && type == 1 && context.isCheckPass() && CollectionUtil.isNotEmpty(insertDetailList)) {
            cmdbDiskDetailRepository.saveAll(insertDetailList);
            cmdbChangeLog(JSON.toJSONString(insertDetailList), DictConstant.ADD_STRING, DictConstant.CMDB_DISK_DETAIL, user.getUsername());
        }
        return context;
    }

    /**
     * cmbc 配件详情 上传文件导入
     */
    @Transactional
    public ReportUploadContext<CmdbCommonDetailImportDto, String> partDetailUpload(MultipartFile file, Integer type, PlatformUserDto user) throws IOException {
        ReportUploadContext<CmdbCommonDetailImportDto, String> context = ReportUploadContext.<CmdbCommonDetailImportDto, String>builder()
                .config(ReportFileConfigEnum.CMDB_PART_DETAIL)
                .file(file)
                .build();
        ReportFileUtil.readSingleExcel(context);
        AssertOverride.notEmpty(context.getReadRes(), "上传内容不可为空");

        List<CmdbCommonDetailImportDto> readResList = context.getReadRes();
        List<CmdbPartDetail> insertDetailList = Lists.newArrayList();
        Map<String, CmdbMachineRoom> roomMap = new HashMap<>();

        for (CmdbCommonDetailImportDto dto : readResList) {
            CmdbPartStandard partStandard = findPartStandardByPartNum(dto.getPartNum());
            AssertOverride.notNull(partStandard, "未查询到规格配件号[" + dto.getPartNum() + "]的规格信息");
            //dto.setPartNum(partStandard.getPartNum());
            if (StringUtils.isNotBlank(dto.getMachineRoom())) {
                if (roomMap.containsKey(dto.getMachineRoom())) {
                    dto.setLocationId(roomMap.get(dto.getMachineRoom()).getId());
                } else {
                    CmdbMachineRoom room = findCmdbMachineRoomByName(dto.getMachineRoom());
                    AssertOverride.notNull(room, "未查询到名称为:" + dto.getMachineRoom() + "的机房信息");
                    dto.setLocationId(room.getId());
                    roomMap.put(dto.getMachineRoom(), room);
                }
            }

            CmdbPartDetail cmdbPartDetail = CmdbPartDetail.builder().partDetailCode(sequenceProductService.buildSequence(DictConstant.CMDB_PART_DETAIL)).partStandardCode(partStandard.getPartStandardCode()).isDelete(0).locationId(dto.getLocationId())
                    .lifeStatus(0).sn(dto.getSn()).build();
            cmdbPartDetail.setExpirateDate(convertDate(dto.getExpirateDate()));
            cmdbPartDetail.setManufactDate(convertDate(dto.getManufactDate()));
            insertDetailList.add(cmdbPartDetail);
        }
        //全部检查通过后添加或修改数据
        if (Objects.nonNull(type) && type == 1 && context.isCheckPass() && CollectionUtil.isNotEmpty(insertDetailList)) {
            cmdbPartDetailRepository.saveAll(insertDetailList);
            cmdbChangeLog(JSON.toJSONString(insertDetailList), DictConstant.ADD_STRING, DictConstant.CMDB_PART_DETAIL, user.getUsername());
        }
        return context;
    }


    /**
     * cmbc 服务器详情 上传文件导入
     */
    @Transactional
    public ReportUploadContext<CmdbDeviceDetailImportDto, String> deviceDetailUpload(MultipartFile file, Integer type, PlatformUserDto user) throws IOException {
        ReportUploadContext<CmdbDeviceDetailImportDto, String> context = ReportUploadContext.<CmdbDeviceDetailImportDto, String>builder()
                .config(ReportFileConfigEnum.CMDB_DEVICE_DETAIL)
                .file(file)
                .build();
        ReportFileUtil.readSingleExcel(context);
        AssertOverride.notEmpty(context.getReadRes(), "上传内容不可为空");

        List<CmdbDeviceDetailImportDto> readResList = context.getReadRes();
        List<CmdbDeviceDetail> insertDetailsList = Lists.newArrayList();

        for (CmdbDeviceDetailImportDto dto : readResList) {
            AssertOverride.isTrue(StringUtils.isNotEmpty(dto.getDeviceStandardName()), "名称为" + dto.getName() + "的服务器信息缺少[设备规格名称]信息");

            List<CmdbDeviceStandard> standards = cmdbDeviceStandardRepository.findAll(Example.of(CmdbDeviceStandard.builder().standardName(dto.getDeviceStandardName()).build()));
            AssertOverride.notEmpty(standards, "未查询到规格名称为[" + dto.getDeviceStandardName() + "]的服务器规格信息");

            AssertOverride.notNull(dto.getDeviceTypeName(), dto.getName() + "的设备类型不可为空");
            CmdbDeviceType deviceType = findCmdbDeviceTypeByName(dto.getDeviceTypeName());
            AssertOverride.notNull(dto.getDeviceTypeName(), "未查询到" + dto.getName() + "的设备类型信息（非法的设备类型）");

            CmdbDeviceDetail deviceDetail = new CmdbDeviceDetail();
            insertDetailsList.add(deviceDetail);
            CmdbDeviceStandard standard = standards.get(0);
            String format = "yyyy-MM-dd";
            deviceDetail = deviceDetail.builder().brand(standard.getBrand()).cpuPortAmount(standard.getCpuPortAmount()).deviceDetailCode(sequenceProductService.buildSequence(DictConstant.CMDB_DEVICE_DETAIL))
                    .deviceStandardCode(standard.getDeviceStandardCode()).deviceType(deviceType).diskPortAmount(standard.getDiskPortAmount()).elePortAmount(standard.getElePortAmount())
                    .inputPriceWithTax(StringUtils.isNotBlank(dto.getInputPriceWithTax()) ? new BigDecimal(dto.getInputPriceWithTax()) : null).isDelete(0).lifeStatus(0)
                    .inputPriceNoTax(StringUtils.isNotBlank(dto.getInputPriceNoTax()) ? new BigDecimal(dto.getInputPriceNoTax()) : null)
                    .purchaseDate(StringUtils.isNotBlank(dto.getPurchaseDate()) ? DateUtil.parseLocalDateTime(dto.getPurchaseDate(), format).toLocalDate() : null)
                    .manufactDate(StringUtils.isNotBlank(dto.getManufactDate()) ? DateUtil.parseLocalDateTime(dto.getManufactDate(), format).toLocalDate() : null)
                    .expirateDate(StringUtils.isNotBlank(dto.getExpirateDate()) ? DateUtil.parseLocalDateTime(dto.getExpirateDate(), format).toLocalDate() : null)
                    //.machineCabinetId().machineCabinetUId()
                    .memoryPortAmount(standard.getMemoryPortAmount())
                    .model(standard.getModel()).name(dto.getName()).pciPortAmount(standard.getPciPortAmount()).sn(dto.getSn()).uAmount(standard.getUAmount()).user(dto.getUser()).build();

            if (StringUtils.isNotBlank(dto.getMachineRoom())) {
                CmdbMachineRoom room = findCmdbMachineRoomByName(dto.getMachineRoom());
                AssertOverride.notNull(room, "未查询到名称为:" + dto.getMachineRoom() + "的机房信息");
                deviceDetail.setMachineRoomId(room.getId());
                dto.setMachineRoomId(room.getId());
            }
            dto.setDeviceStandardCode(standard.getDeviceStandardCode());
            dto.setDeviceTypeId(deviceType.getDeviceTypeId());
            dto.setModel(standard.getModel());

        }
        //全部检查通过后添加或修改数据
        if (Objects.nonNull(type) && type == 1 && context.isCheckPass() && CollectionUtil.isNotEmpty(insertDetailsList)) {
            cmdbDeviceDetailRepository.saveAll(insertDetailsList);
            cmdbChangeLog(JSON.toJSONString(insertDetailsList), DictConstant.ADD_STRING, DictConstant.CMDB_DEVICE_DETAIL, user.getUsername());
        }
        return context;
    }

    @Transactional
    public List<CmdbDeviceDetail> deviceScrapUpload(MultipartFile file) throws IOException {
        ReportUploadContext<CmdbDeviceScrapImportDto, String> context = ReportUploadContext.<CmdbDeviceScrapImportDto, String>builder()
                .config(ReportFileConfigEnum.CMDB_DEVICE_SCRAP)
                .file(file)
                .build();
        ReportFileUtil.readSingleExcel(context);
        AssertOverride.notEmpty(context.getReadRes(), "上传内容不可为空");
        List<String> snList = context.getReadRes().stream().map(CmdbDeviceScrapImportDto::getSn).collect(Collectors.toList());
        List<CmdbDeviceDetail> snDeviceList = cmdbDeviceDetailRepository.findAllBySnIn(snList);
        List<CmdbDeviceDetail> deviceList = cmdbDeviceDetailRepository.findAllById(snDeviceList.stream().map(CmdbDeviceDetail::getDeviceDetailId).collect(Collectors.toList()));
        return deviceList;
    }

    public CmdbPartStandardDeviceStandardRelate convertCmdbPartStandardDeviceStandardRelate(String partNum, String title, CmdbDeviceStandardImportDto e, String deviceStandardCode, Integer amount) {
        if (StringUtils.isNotBlank(partNum)) {
            CmdbPartStandard gpuCage = findPartStandardByPartNum(partNum);
            AssertOverride.notNull(gpuCage, "未查询到规格配件号[" + e.getGpuCagePartNum() + "]的" + title + "规格信息");

            CmdbPartStandardDeviceStandardRelate cmdbPartStandardDeviceStandardRelate = CmdbPartStandardDeviceStandardRelate.builder().amount(amount)
                    .partStandardCode(gpuCage.getPartStandardCode())
                    .isDelete(0).deviceStandardCode(deviceStandardCode).build();
            cmdbPartStandardDeviceStandardRelate.setPartStandardName(gpuCage.getName());
            cmdbPartStandardDeviceStandardRelate.setPartTypeName(Objects.nonNull(gpuCage.getCmdbPartType()) ? gpuCage.getCmdbPartType().getPartTypeName() : null);
            return cmdbPartStandardDeviceStandardRelate;
        }
        return null;
    }

    public CmdbCpuStandard findCpuStandardByPartNum(String partNum) {
        List<CmdbCpuStandard> cpuStandardList = cmdbCpuStandardRepository.findAll(Example.of(CmdbCpuStandard.builder().partNum(partNum).build()));
        return CollectionUtil.isEmpty(cpuStandardList) ? null : cpuStandardList.get(0);
    }

    public CmdbMemoryStandard findMemoryStandardByPartNum(String partNum) {
        List<CmdbMemoryStandard> memoryStandardList = cmdbMemoryStandardRepository.findAll(Example.of(CmdbMemoryStandard.builder().partNum(partNum).build()));
        return CollectionUtil.isEmpty(memoryStandardList) ? null : memoryStandardList.get(0);
    }

    public CmdbDiskStandard findDiskStandardByPartNum(String partNum) {
        List<CmdbDiskStandard> diskStandardList = cmdbDiskStandardRepository.findAll(Example.of(CmdbDiskStandard.builder().partNum(partNum).build()));
        return CollectionUtil.isEmpty(diskStandardList) ? null : diskStandardList.get(0);
    }

    public CmdbPartStandard findPartStandardByPartNum(String partNum) {
        List<CmdbPartStandard> partStandardList = cmdbPartStandardRepository.findAll(Example.of(CmdbPartStandard.builder().partNum(partNum).build()));
        return CollectionUtil.isEmpty(partStandardList) ? null : partStandardList.get(0);
    }

    public CmdbDeviceType findCmdbDeviceTypeByName(String typeName) {
        List<CmdbDeviceType> partStandardList = cmdbDeviceTypeRepository.findAll(Example.of(CmdbDeviceType.builder().deviceTypeName(typeName).build()));
        return CollectionUtil.isEmpty(partStandardList) ? null : partStandardList.get(0);
    }

    public CmdbCpuStandardDeviceStandardRelate findCmdbCpuStandardDeviceStandardRelateByDeviceCode(String deviceCode) {
        List<CmdbCpuStandardDeviceStandardRelate> relateList = cmdbCpuStandardDeviceStandardRelateRepository.findAll(Example.of(CmdbCpuStandardDeviceStandardRelate.builder().deviceStandardCode(deviceCode).build()));
        return CollectionUtil.isEmpty(relateList) ? null : relateList.get(0);
    }

    public CmdbMemoryStandardDeviceStandardRelate findCmdbMemoryStandardDeviceStandardRelateByDeviceCode(String deviceCode) {
        List<CmdbMemoryStandardDeviceStandardRelate> relateList = cmdbMemoryStandardDeviceStandardRelateRepository.findAll(Example.of(CmdbMemoryStandardDeviceStandardRelate.builder().deviceStandardCode(deviceCode).build()));
        return CollectionUtil.isEmpty(relateList) ? null : relateList.get(0);
    }

    public CmdbDiskStandardDeviceStandardRelate findDiskMemoryStandardDeviceStandardRelateByDeviceCode(String deviceCode) {
        List<CmdbDiskStandardDeviceStandardRelate> relateList = cmdbDiskStandardDeviceStandardRelateRepository.findAll(Example.of(CmdbDiskStandardDeviceStandardRelate.builder().deviceStandardCode(deviceCode).build()));
        return CollectionUtil.isEmpty(relateList) ? null : relateList.get(0);
    }

    public CmdbPartStandardDeviceStandardRelate findDiskPartStandardDeviceStandardRelateByDeviceCode(String deviceCode) {
        List<CmdbPartStandardDeviceStandardRelate> relateList = cmdbPartStandardDeviceStandardRelateRepository.findAll(Example.of(CmdbPartStandardDeviceStandardRelate.builder().deviceStandardCode(deviceCode).build()));
        return CollectionUtil.isEmpty(relateList) ? null : relateList.get(0);
    }

    public CmdbMachineRoom findCmdbMachineRoomByName(String name) {
        List<CmdbMachineRoom> roomLis = cmdbMachineRoomRepository.findAll(Example.of(CmdbMachineRoom.builder().name(name).build()));
        return CollectionUtil.isEmpty(roomLis) ? null : roomLis.get(0);
    }

    public void cmdbChangeLog(String changeContent, String changType, String changeFrom, String account) {
        CmdbChangeLog log = new CmdbChangeLog();
        log.setChangeContent(changeContent);
        log.setChangeType(changType);
        log.setChangeFrom(changeFrom);
        log.setChangeUser(account);
        cmdbChangeLogRepository.save(log);
    }

}
